package edu.pku.sei.pgie.analyzer.core;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;

import edu.pku.sei.pgie.analyzer.internal.CodeEntityMetricsAnalyzer;
import edu.pku.sei.pgie.analyzer.internal.CodeEntityOverrideAnalyzer;
import edu.pku.sei.pgie.analyzer.internal.CodeEntityRelationAnalyzer;
import edu.pku.sei.pgie.analyzer.internal.CodeEntitySymbolTable;
import edu.pku.sei.pgie.analyzer.internal.CodeEntityTextAnalyzer;
import edu.pku.sei.pgie.analyzer.internal.RepositoryStructureAnalyzer;
import edu.pku.sei.pgie.analyzer.internal.SourceTreeProgressListener;
import edu.pku.sei.pgie.ui.actions.LoopRelationFilter;
import edu.pku.sei.pgie.ui.actions.SymbolTableFilter;

public class Analyzer {
    Context context;

    public Analyzer(Context context) {
        this.context = context;
    }

    public void anlayze(IProgressMonitor monitor) throws Exception{
        if(context == null || context.getJavaProject() == null) {
            throw new RuntimeException("context not set properly");
        }

        if(monitor == null) {
            monitor = new NullProgressMonitor();
        }

        int steps = 3;
        steps += context.isEnableOverrideAnalyze() ? 1 : 0;
        steps += context.isEnableStructureAnalyze() ? 1 : 0;

        SourceTree sourceTree = new SourceTree(context.getJavaProject());

        monitor.beginTask("Source Code Processing", steps);
        if(context.isEnableStructureAnalyze()) {
            monitor.setTaskName("Analyzer Repository Structure");
            RepositoryStructureAnalyzer structureAnalyzer = new RepositoryStructureAnalyzer(context);
            structureAnalyzer
                    .registerListener(new SourceTreeProgressListener(
                            monitor));
            sourceTree.accept(structureAnalyzer);
            structureAnalyzer.flushCache();
            monitor.worked(1);
        }

        monitor.setTaskName("Text Analyze");
        CodeEntityTextAnalyzer textAnalyzer = new CodeEntityTextAnalyzer(
                context);
        textAnalyzer
                .registerListener(new SourceTreeProgressListener(
                        monitor));
        sourceTree.accept(textAnalyzer);
        monitor.worked(1);
        CodeEntitySymbolTable symbolTable = textAnalyzer
        .getSymbolTable();

        monitor.setTaskName("Metrics Analyze");
        CodeEntityMetricsAnalyzer metricsAnalyzer = new CodeEntityMetricsAnalyzer(context, symbolTable);
        metricsAnalyzer.registerListener(new SourceTreeProgressListener(monitor));
        sourceTree.accept(metricsAnalyzer);
        metricsAnalyzer.collect();
        monitor.worked(1);

        if(context.isEnableOverrideAnalyze()) {
            monitor.setTaskName("Analyze Override Relation");
            CodeEntityOverrideAnalyzer overrideAnalyzer = new CodeEntityOverrideAnalyzer(context);
            final List<CompilationUnit> compilationUnits = new ArrayList<CompilationUnit>();

            final ASTParser parser = ASTParser.newParser(AST.JLS3);
            parser.setProject(context.getJavaProject());

            SourceTreeVisitor compilationUnitCollector = new SourceTreeVisitor(){
                public boolean visit(ICompilationUnit compilationUnit) {
                    if (compilationUnit == null)
                        return false;
                    parser.setSource(compilationUnit);
                    parser.setResolveBindings(true);
                    CompilationUnit root = (CompilationUnit) parser.createAST(null);
                    compilationUnits.add(root);
                    return true;
                }
            };

            sourceTree.accept(compilationUnitCollector);
            overrideAnalyzer.analyze(compilationUnits.toArray(new CompilationUnit[compilationUnits.size()]), symbolTable);
            monitor.worked(1);
        }

        CodeEntityRelationAnalyzer relationAnalyzer = new CodeEntityRelationAnalyzer(
                symbolTable,context);
        relationAnalyzer.registerFilter(new SymbolTableFilter(
                symbolTable));
        relationAnalyzer.registerFilter(new LoopRelationFilter());
        relationAnalyzer
                .registerListener(new SourceTreeProgressListener(
                        monitor));
        monitor.setTaskName("Relation Analyzer");
        sourceTree.accept(relationAnalyzer);
        monitor.worked(1);

        monitor.done();


    }
}
